package com.snts.synchronization.core;

import java.awt.MultipleGradientPaint.ColorSpaceType;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.snts.synchronization.garbage.Utils;

public class XMLLoader extends DefaultHandler {

	private static final Log log = LogFactory.getLog(XMLLoader.class);
	
	private static final String INTERNAL_UPDATE_WINS = "internal-update-wins";
	private static final String COLUMN = "column";
	private static final String TABLE = "table";
	private static final String EXTERNAL_TABLE = "external-table";
	private static final String TABLE_REFERENCE = "table-reference";
	private static final String COMPUTE_EXTERNAL_COLUMN_NAME = "compute-external-column-name";
	private static final String PRIMARY_KEY_COLUMN = "primary-key-column";
	private static final String QUOTE_IDENTIFIER = "quote-identifier";
	
	private Synchronization synchronization;
	private SyncTable table;
	private Column column;
	private boolean internalUpdateWins = false;
	private String qName;
	
	private StringBuffer buf = null;
	private boolean computeExternalColumnName = false;
	

	public XMLLoader(Synchronization configuration) {
		this.synchronization = configuration;
	}

	public XMLLoader load() {
		try {
			String uri = getClass().getClassLoader().getResource("synchronization.xml").toString();
			SAXParser parser = Utils.getSAXParser();
			parser.parse(uri, this);
		} catch (Throwable t) {
			t.printStackTrace();
			throw new RuntimeException("Exception loading configuration", t);
		}
		return this;
	}

	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		if(buf != null){
			buf.append(ch, start, length);
		}
	}

	@Override
	public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
		qName = qName.intern();
		this.qName = qName;
		if (COLUMN == qName) {
			column = new Column();
			table.addColumn(column);
			column.name = attributes.getValue("name");
			if (attributes.getValue("external-name") != null) {
				column.externalName = attributes.getValue("external-name");
			} else if (computeExternalColumnName) {
				column.externalName = Utils.computeColumnName(column.name);
			} else {
				column.externalName = column.name;
			}
			String syncDirection = attributes.getValue("sync-direction");
			if ("outside".equals(syncDirection)) {
				column.syncDirection = Column.SyncDirection.SD_OUTSIDE;
			} else if ("inside".equals(syncDirection)) {
				column.syncDirection = Column.SyncDirection.SD_INSIDE;
			} else if ("both".equals(syncDirection)) {
				column.syncDirection = Column.SyncDirection.SD_BOTH;
			} else {
				column.syncDirection = Column.SyncDirection.SD_BOTH;
			}
		} else if (TABLE == qName) {
			table = new SyncTable();
			if(attributes.getValue("catalog") != null){
				table.catalog = attributes.getValue("catalog");
			}
			if(attributes.getValue("schema") != null){
				table.schema = attributes.getValue("schema");
			} else {
				table.schema = "public";
			}
			if(attributes.getValue("update-appends-log") != null){
				synchronization.setUpdateAppendsLog(Boolean.parseBoolean(attributes.getValue("update-appends-log")));
			}
			table.name = attributes.getValue("name");
		} else if (EXTERNAL_TABLE == qName) {
			if(attributes.getValue("schema") != null){
				table.externalTable.schema = attributes.getValue("schema");
			} else {
				table.externalTable.schema = "public";
			}
			if(attributes.getValue("name") == null){
				log.fatal("name attribute must be set for " + EXTERNAL_TABLE + " tag");
				throw new SAXException("name attribute must be set for " + EXTERNAL_TABLE + " tag");
			}
			table.externalTable.name = attributes.getValue("name");
		} else if (INTERNAL_UPDATE_WINS == qName) {
			String iuw = attributes.getValue("value");
			if (iuw == null || "false".equals(iuw)) {
				internalUpdateWins = false;
			} else if ("true".equals(iuw)) {
				internalUpdateWins = true;
			}
		} else if (TABLE_REFERENCE == qName) {
			String schema = "public";
			if(attributes.getValue("schema") != null){
				schema = attributes.getValue("schema");
			}
			TableReference tableReference = new TableReference(schema, attributes.getValue("table-name"), synchronization.isQuoteIdentifier());			
			tableReference.setForeignKey(attributes.getValue("foreign-key"));
			table.joinRoute.add(tableReference);
		} else if(COMPUTE_EXTERNAL_COLUMN_NAME == qName){
			computeExternalColumnName = true;
		} else if(PRIMARY_KEY_COLUMN == qName){
			buf = new StringBuffer();
		} else if(QUOTE_IDENTIFIER == qName){
			if(attributes.getValue("value") != null){
				synchronization.setQuoteIdentifier(Boolean.parseBoolean(attributes.getValue("value")));
			}
		}

		/*
		 * System.out.println("startElement(uri=" + uri + "," + "localName=" +
		 * localName + ", qName=" + qName + ");"); for(int i = 0; i <
		 * attributes.getLength(); i ++){
		 * System.out.println(attributes.getQName(i) + "=" +
		 * attributes.getValue(attributes.getQName(i))); }
		 */

	}
	
	public static HashSet<Integer> us = new HashSet<Integer>();

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		if (COLUMN.equals(qName)) {
			column = null;
		} else if (TABLE.equals(qName)) {
			synchronization.addTable(table);
			table = null;
		} else if(PRIMARY_KEY_COLUMN.equals(qName)){
			synchronization.setPkColumnName(buf.toString().trim());
			this.qName = null;
			this.buf = null;
		}
	}

	public static void main(String[] args) throws URISyntaxException {
		XMLLoader loader = new XMLLoader(new Synchronization()).load();
		for (SyncTable t : loader.synchronization.getTables()) {
			System.out.println("<table name=" + t.name  + ">");
			System.out.println("  <join-route>");
			for (TableReference jri : t.joinRoute) {
				System.out
						.println("        <join-route-item tableName=" + jri.getTableName() + " joinColumn=" + jri.getForeignKey() + "/>");
			}
			System.out.println("  </join-route>");
			for (Column c : t.getColumns()) {
				System.out.println("    <column name=" + c.name + " external-name=" + c.externalName + " external-table-name="
						+ c.externalTableName + " sync-direction=" + c.syncDirection.name() + " />");
			}
		}
	}

}
